developmentor - a developer services company				  
				      Essential .NET Security
   Modules

Public Key Crypto

Estimated time for completion: 60 minutes


Goals of this exercise:

  • Request certificates from a local certificate authority
  • Practice creating PKCS#7 messages
  • Create signed and enveloped (encrypted) data
  • Install and use CAPICOM from .NET

Overview

In this activity, you'll build a socket-based architecture for sending and receiving signed and encrypted messages. To do this, you'll need to first obtain a certificate (the instructor will provide a certificate authority). You'll also install the capicom.dll redistributable and use it from your C# code via COM Interop.


part 1- Requesting and exchanging certificates

In this part, you'll request a certificate from a local certificate authority provided by the instructor. You will then exchange certificates with your friends so that you can send secret messages to them.

steps:

  1. Surf to http://somemachine/certsrv, where [somemachine] will be provided by the instructor.

  2. Use the certificate server's web interface (to which you just surfed) to request a user certificate.

  3. Once your certificate has been approved (the instructor will let you know when he's issued it), surf back to http://somemachine/certsrv and choose "Check on a pending certificate". This option will allow you to download and install your new certificate.

  4. Once you've installed your cert, run certmgr.exe and make sure your new cert shows up under the Personal section, known to programmers as the "MY" certificate store. Double click your cert from this window to view it, and note that you have the corresponding private key.

  5. Obtain certificates for any partners you want to work with. Your instructor will help you do this (typically the easiest way to do this is to have the instructor export all certificates to a common share on the server). To import a certificate from a share, just double-click the .CER file on the share. Do not import your own certificate this way, just import certs for your partner(s).

  6. Run certmgr.exe (you'll need to have your Visual Studio environment set up to run this from the command line). You can also get to this tool via Internet Explorer tools dialog, via the Content page, Certificates button. Verify that your certificate shows up under the "Personal" tab, which is known to programmers as the "MY" store. Also verify that any other certs you imported show up under "Other People", which is known to programmers as the "AddressBook" store.


part 2- Installing CAPICOM

In order for us to do our work, we need to install and register capicom.dll. This is a simple step, as the DLL has been provided with your lab set.

steps:

  1. Locate capicom.dll in the redist directory of your labs. With administrator privileges, run regsvr32.exe on this file (e.g. regsvr32 c:\labs\redist\capicom.dll).


part 3- Implementing the listener

In this part, you'll implement a simple listener that listens on a socket for incoming messages. The listener expects each message to be signed by the sender and encrypted so that you are one of the recipients. It decrypts the message using your personal certificate, prints out the message, and shows who signed the message and whether their signatures and certificates are valid.

steps:

  1. Open up the skeleton solution, publicKeyCrypto.sln. Drill down into the listener project and open up the file listener.cs. This is where you'll do all your work for this part.

  2. Examine listener.cs and see what's already provided for you. Note that Main has already been written as a simple blocking socket listener that harvests connections and reads incoming messages into a string variable. Your job is to implement the _handleMessage function to deal with each message as it arrives.

  3. The first thing to do in _handleMessage is to decrypt the incoming ciphertext. Since our senders will be using PKCS#7 message formatting, this is really easy to do. You'll be using CAPICOM, so under the listener project, right click "References" and add a reference to capicom.dll (the one you just registered). Add a using statement also:

    using CAPICOM;
  4. Create an instance of EnvelopedData, and call Decrypt to decrypt the ciphertext. If this fails, you'll see an exception thrown, otherwise, the properties of EnvelopedData will now be available to you: Content, Algorithm, etc.

  5. Now that you've decrypted the message, you need to verify the sender's signature. So create a SignedData object and call Verify, passing the Content of the EnvelopedData object, false for the second argument (bDetached), and CAPICOM_SIGNED_DATA_VERIFY_FLAG.CAPICOM_VERIFY_SIGNATURE_AND_CERTIFICATE for the last argument, indicating that you want to validate the signature and corresponding certificates.

  6. If Verify didn't throw any exceptions, then the properties of the SignedData object are now available to you: Content, Signers, etc. Use the Content property of the SignedData object to print out the plaintext message to the console.

  7. Loop through the SignedData.Signers collection and look at the Certificate property for each Signer. Call the IsValid() method on each one (there will only be one in this lab) and check the Result property (signer.Certificate.IsValid().Result). This is a boolean that tells you whether the signature and certificate is valid (sans revocation checking). For each signer, print out the Certificate.SubjectName field and whether or not the signature of that signer is valid.

    If you find an invalid signer, you should print out a big rude message so the user will know that the content may have been forged by a bad guy.

  8. Build your listener (don't start it just yet).


part 4- Implementing the sender

In this part, you'll implement a simple socket-based sender that sends encrypted and signed messages to any listener whose hostname or IP address you happen to know. The sender sends PKCS#7 messages, so once again, you'll be using CAPICOM.

steps:

  1. This time, drill down into the sendmsg project and have a look at the sendmsg.cs file. This is where you'll be doing all of your work for this part.

  2. Examine the work that has already been done for you. Main has been implemented to expect a couple of command line arguments: the host name or dotted decimal ip address of whatever listener to which your message should be sent, and the name of a file containing the message. This should be a small text file, as the listener will simply be dumping the message out to the console.

    Note that the socket connection code has already been provided in the _connectToServer function, and whatever string is returned from _constructMessage will be sent to the target listener.

    _constructMessage is where you'll be doing all your work.

  3. The first thing you'll need to do is add a reference to capicom.dll (and a using statement for CAPICOM) for the sendmsg project, just like you did for your listener project.

  4. An important point to remember is that for security, you always want to sign plaintext, not ciphertext, if you have a choice. Therefore, in _constructMessage, the first thing to do is create a SignedData object, and set the Content property to the plaintext that was passed to you.

  5. Now create a Signer object (this represents your signature). Since you might have more than one personal certificate, call the (already supplied) _selectCert function to prompt the user with a list of personal certificates. Pass "MY" as the name of the store, and false to indicate that you only need one certificate. To make your program a bit more user friendly, you should print out a message like "Please select one of your personal certificates to use for signing the message" before calling _selectCert.

    Assign the returned certificate to the Signer.Certificate property. You're now ready to sign the message.

  6. Call SignedData.Sign, passing your Signer object as the first argument, false for the second argument (bDetached), and for the third argument, specify CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64 to select Base64 encoding (as opposed to raw ASN.1), since we're treating all these messages as strings. The signed message will be returned as the result of Sign, so hold onto it in a string variable for use in the next step.

  7. We're now going to envelope the signed message so that nobody except the intended recipients can decrypt it. Create an EnvelopedData object and set the Content property to the signed message you just created.

  8. Now, ask the user to pick a set of recipients for the message. These are folks whose certificates we have obtained, and who we would like to be able to decrypt the message. Just put yourself into a loop, calling _selectCert("AddressBook", true) until _selectCert returns null (which indicates that the user is done choosing recipients). For each certificate chosen, add it to the EnvelopedData.Recipients collection. To make your program a bit more user friendly, you should print out a message like "Please select one or more certificates representing those who should be able to decrypt the message" before calling _selectCert.

  9. Now you can choose an encryption algorithm and key length to use. To do this, set the EnvelopedData.Algorithm.Name and KeyLength properties. In my solution, I chose the following:

    CAPICOM_ENCRYPTION_ALGORITHM.CAPICOM_ENCRYPTION_ALGORITHM_RC2;
    CAPICOM_ENCRYPTION_KEY_LENGTH.CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS;
    
  10. Now you're ready to encrypt the data. To do this, call EnvelopedData.Encrypt, passing CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BASE64 as the single argument, since we're working with strings. This is the encrypted message, so return this from the function you are implementing, and you're done!

  11. Build and test with a partner.

Solutions: